package client;


import java.io.*;
import java.net.*;

import client.gui.util.GUIChanger;
import client.heartbeat.ClientHeartbeat;
import client.heartbeat.ServerHeartbeatListener;


import util.ClientProtocol;
import util.Logger;

public class TcpClient {
	public static volatile int clientSequenceId;
	public static volatile int clientReferenceId;
	private Client client;
	private Socket requestSocket;
	private ObjectOutputStream out;
	private ObjectInputStream in;
	private ClientHeartbeat heartbeat;
	private ServerHeartbeatListener serverHeartbeatListener;
	private GUIChanger guiChanger;

	public TcpClient(Client client) {
		this.client = client;
		guiChanger = new GUIChanger(this);
	}
	
	public void connect(String host, int port) {
		try {
			requestSocket = new Socket(host, port);
			out = new ObjectOutputStream(requestSocket.getOutputStream());
			in  = new ObjectInputStream(requestSocket.getInputStream());
			
			sendMessage("login=" + client.getName());
			
		} catch (IOException e) {
			Logger.logClientError(client.getName(),"server is not available");
		}
	}
	
	public void heartbeatStart() {
		heartbeat = new ClientHeartbeat(this);
		heartbeat.start();
		serverHeartbeatListener = new ServerHeartbeatListener(this);
		serverHeartbeatListener.start();
	}
	
	public void start() {
		heartbeatStart();
		new Thread(new Runnable() {
			@Override
			public void run() {
				while(!Thread.interrupted()){
					receiveMessage();
				}
			}		
		}).start();
	}
	
	public String receiveMessage() {
		try {
			final String msg = (String)in.readObject();
			if(msg.matches(ClientProtocol.HEARTBEAT_MATCHER)) {
				client.setAlive(true);
			} else {
				guiChanger.update(msg);
			}
			Logger.logServerInfo(msg);
			return msg;
		} catch (ClassNotFoundException | IOException e) {
			Logger.logClientError(this.client.getName(),"server is not available, disconnecting.");
			Thread.currentThread().interrupt();
			disconnect();
		} catch(NullPointerException e) {
			Logger.logClientError(this.client.getName(),"NullPointerException");
			Thread.currentThread().interrupt();
		}
		return null;
	}
	
	public void sendMessage(String msg) {
		try {
			synchronized(out) {
				out.writeObject("sequenceId=" + (clientSequenceId++) + ";" + msg);
				out.flush();				
			}
			Logger.logClientInfo(this.getClient().getName(), msg);				
		} catch (IOException e) {
			Logger.logClientError(this.client.getName(),"server is not available, disconnecting.");
			disconnect();
			Thread.currentThread().interrupt();
		} catch (NullPointerException e) {
			Logger.logClientError(this.client.getName(),"NullPointerException");
			disconnect();
			Thread.currentThread().interrupt();
		}
	}	

	public void disconnect() {
		try {
			in.close();
		} catch (IOException e) {
			Logger.logClientError(this.client.getName(),"INPUT cannot be closed");
			Thread.currentThread().interrupt();
		}
		try {
			out.close();
		} catch (IOException e) {
			Logger.logClientError(this.client.getName(),"> OUTPUT cannot be closed");
			Thread.currentThread().interrupt();
		}
		try {
			requestSocket.close();
		} catch (IOException e) {
			Logger.logClientError(this.client.getName(),"> SOCKET cannot be closed");
			Thread.currentThread().interrupt();
		}
	}
	
	public Client getClient() {
		return this.client;
	}
	
	public GUIChanger getGuiChanger() {
		return guiChanger;
	}
	
}
